package locadora.negocio.pessoas;
import java.io.Serializable;


import locadora.negocio.Buscavel;
import miscelanea.Data;

public class Pessoa implements Buscavel, Serializable{
	
	private static final long serialVersionUID = 1L;
	public static final int TAM_MAX_ESTADO = 15;
	public static final int NUM_DIG_CEP    =  8;
	public static final int TAM_MAX_NOME   = 80;
	public static final int TAM_MAX_IDENT  = 16;
	public static final int TAM_MAX_EXPED  = 16;
	public static final int TAM_MAX_LOGRD  = 64;
	public static final int TAM_CPF		   = 11;
	public static final int TAM_MAX_CIDADE = 32;
	public static final int TAM_MAX_BAIRRO = 32;
	
	protected String		nome;
	protected Data			dataNascimento;
	protected String		identidade;
	protected String		orgaoExpedidor;
	protected String		CPF;
	protected String		logradouro;
	protected String		CEP;
	protected String		bairro;
	protected String		cidade;
	protected String		estado;
	
	/**
	 * Cria uma instância de Pessoa
	 * @param nome nome da pessoa
	 * @param dataNascimento data de nascimento da pessoa
	 * @param identidade número da identidade no formato XXXXX, onde X é um algarismo
	 * @param orgaoExpedidor nome do órgão expedidor
	 * @param CPF número do CPF no formato XXXXXXXXXXX, no qual X é um algarismo
	 * @param logradouro nome do logradouro
	 * @param CEP CEP no formato XXXXXXXX, no qual X é um algarismo
	 * @param bairro nome do bairro
	 * @param cidade nome da cidade
	 * @param estado nome do estado
	 * @throws Exception mensagens de erro indicando o erro específico
	 */
	public Pessoa(String nome, Data dataNascimento, String identidade, String expedidor,
			String CPF, String logradouro, String CEP, String bairro, String cidade, String estado) throws Exception{
		this.nome = limitaTamanhoNome(nome);
		this.dataNascimento = dataNascimento;
		this.identidade = limitaTamanhoIdentidade(identidade);
		this.orgaoExpedidor = validaOrgaoExpedidor(expedidor);
		this.CPF = validaCPF(CPF);
		this.logradouro = limitaTamanhoLogradouro(logradouro);
		this.CEP = validaCEP(CEP);
		this.bairro = limitaTamanhoBairro(bairro);
		this.cidade = limitaTamanhoCidade(cidade);
		this.estado = limitaTamanhoEstado(estado);
	}
	
	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_ESTADO caracteres ou null, caso o parâmetro possua este valor
	 * @param estado nome do estado
	 * @return nome do estado ou null
	 */
	protected String limitaTamanhoEstado(String estado) {
		String resultado = null;
		if(estado != null){
			int tamanho = estado.length();
			if(tamanho > TAM_MAX_ESTADO)
				tamanho = TAM_MAX_ESTADO;
			resultado = estado.substring(0, tamanho);
		}
		return resultado;
	}

	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_CIDADE caracteres ou null, caso o parâmetro possua este valor
	 * @param cidade órgão expedidor
	 * @return nome da cidade ou null
	 */
	protected String limitaTamanhoCidade(String cidade) {
		String resultado = null;
		if(cidade != null){
			int tamanho = cidade.length();
			if(tamanho > TAM_MAX_CIDADE)
				tamanho = TAM_MAX_CIDADE;
			resultado = cidade.substring(0, tamanho);
		}
		return resultado;
	}

	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_BAIRRO caracteres ou null, caso o parâmetro possua este valor
	 * @param bairro nome do bairro
	 * @return nome do bairro ou null
	 */
	protected String limitaTamanhoBairro(String bairro) {
		String resultado = null;
		if(bairro != null){
			int tamanho = bairro.length();
			if(tamanho > TAM_MAX_BAIRRO)
				tamanho = TAM_MAX_BAIRRO;
			resultado = bairro.substring(0, tamanho);
		}
		return resultado;
	}
	
	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_LOGRD caracteres ou null, caso o parâmetro possua este valor
	 * @param logradouro nome logradoouro
	 * @return nome do logradouro ou null
	 */
	protected String limitaTamanhoLogradouro(String logradouro) {
		String resultado = null;
		if(logradouro != null){
			int tamanho = logradouro.length();
			if(tamanho > TAM_MAX_LOGRD)
				tamanho = TAM_MAX_LOGRD;
			resultado = logradouro.substring(0, tamanho);
		}
		return resultado;
	}

	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_EXPED caracteres ou null, caso o parâmetro possua este valor
	 * @param expedidor nome do órgão expedidor
	 * @return nome do órgão ou null
	 */
	protected String limitaTamanhoOrgaoExpedidor(String expedidor) {
		String resultado = null;
		if(expedidor != null){
			int tamanho = expedidor.length();
			if(tamanho > TAM_MAX_EXPED)
				tamanho = TAM_MAX_EXPED;
			expedidor = expedidor.substring(0, tamanho);
			resultado = expedidor;
		}
		return resultado;
	}
	
	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_EXPED caracteres ou null, caso o parâmetro possua este valor
	 * @param orgaoExpedidor órgão expedidor
	 * @return nome do órgão ou null
	 * @throws Exception
	 */
	protected String validaOrgaoExpedidor(String orgaoExpedidor) throws Exception {
		String expedidor = limitaTamanhoOrgaoExpedidor(orgaoExpedidor);
		if((identidade == null) && (expedidor != null)){
			throw new Exception("O órgão expedidor não pode ser nulo");
		}
		return expedidor;
	}

	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_IDENT caracteres ou null, caso o parâmetro possua este valor
	 * @param identidade numero da identidade
	 * @return número da identidade
	 */
	protected String limitaTamanhoIdentidade(String identidade) {
		String resultado = null;
		if(identidade != null){
			int tamanho = identidade.length();
			if(tamanho > TAM_MAX_IDENT)
				tamanho = TAM_MAX_IDENT;
			resultado = identidade.substring(0, tamanho);
		}
		return resultado;
	}

	/**
	 * Retorna o parâmetro de entrada limitado aos primeiros TAM_MAX_NOME caracteres ou null, caso o parâmetro possua este valor
	 * @param nome nome da pessoa
	 * @return nome da pessoa
	 */
	protected String limitaTamanhoNome(String nome) {
		String resultado = null;
		if(nome != null);
			int tamanho = nome.length();
			if(tamanho > TAM_MAX_NOME)
				tamanho = TAM_MAX_NOME;
			resultado = nome.substring(0, tamanho);
		return resultado;
	}
	

	/**
	 * Recebe um CPF e verifica se o mesmo é válido de acordo com o cálculo dos dígitos verificadores,
	 * exatamente como ocorre com o processo de validação realizado pela RFB ou null, caso o parâmetro possua este valor
	 * @param CPF parâmetro no formato XXXXXXXXXXX, no qual X representa um algarismo
	 * @return Exatamente o mesmo valor da entrada caso esteja em conformidade com o padrão ou null se a String de entrada possuir este valor
	 * @throws Exception mensagem de erro
	 */
	protected String validaCPF(String CPF) throws Exception{
		
		if(CPF != null){
			int[] pesosPrimeiroDigitoVerificador = {10,9,8,7,6,5,4,3,2};
			int[] pesosSegundoDigitoVerificador = {11,10,9,8,7,6,5,4,3,2};
			int[] cpf = {0,0,0,0,0,0,0,0,0,0,0};
			
			if(CPF.length() == TAM_CPF){
				int somaComPesos = 0;
				
				for(int i = 0; i < TAM_CPF - 2; i++){
					cpf[i] = Integer.parseInt(String.format("%c", CPF.charAt(i)));
					somaComPesos = somaComPesos + cpf[i]*pesosPrimeiroDigitoVerificador[i];
				}
				
				//Calcula o primeiro dígito verificador
				if((somaComPesos % TAM_CPF) < 2)
					cpf[TAM_CPF - 2] = 0;
				else
					cpf[TAM_CPF - 2] = TAM_CPF - (somaComPesos % TAM_CPF);
				
				somaComPesos = 0;
				
				for(int i = 0; i < TAM_CPF - 1; i++){
					somaComPesos = somaComPesos + cpf[i]*pesosSegundoDigitoVerificador[i];
				}
				
				//Calcula o segundo dígito verificador
				if((somaComPesos % TAM_CPF) < 2)
					cpf[TAM_CPF - 1] = 0;
				else
					cpf[TAM_CPF - 1] = TAM_CPF - (somaComPesos % TAM_CPF);
				
				//Verifica se os dígitos verificadores conferem com os dois da entrada
				if(cpf[TAM_CPF - 2] != Integer.parseInt(String.format("%c", CPF.charAt(TAM_CPF - 2))) ||
						cpf[TAM_CPF - 1] != Integer.parseInt(String.format("%c", CPF.charAt(TAM_CPF - 1)))){
					throw new Exception("O CPF não é válido");
				}
				
			}else{
				throw new Exception("CPF inválido!!");
			}	
		}
		
		//retorna null se CPF tiver sido passado dessa maneira ou a propria string
		//de entrada caso nenhuma exceção tenha sido levantada
		return CPF;
	}
	
	/**
	 * Recebe um CEP e verifica se o número de algarismos está de acordo com o padrão brasileiro
	 * @param CEP parâmetro no formato XXXXXXXX, no qual X representa um algarismo, ou null, caso o parâmetro possua este valor
	 * @return Exatamente o mesmo valor da entrada caso esteja em conformidade com o padrão
	 * @throws Exception mensagem de erro
	 */
	protected String validaCEP(String CEP) throws Exception{
		
		if(CEP != null){
			if(CEP.length() != NUM_DIG_CEP){
				throw new Exception();
			}else{
				int cepComoInteiro = 0;

				cepComoInteiro = Integer.decode(CEP);
				if(cepComoInteiro <= 0){
					throw new Exception("CEP com formato inválido!!");
				}
			}
		}
		return CEP;
	}

	public String toString(){
		return "\n Nome: " + this.nome + "\n Nascimento:\t" + this.dataNascimento + "\n Identidade: " + this.identidade + "\n Expedidor: " + this.orgaoExpedidor + "\n CPF: " + this.CPF + "\n Logradouro: " + this.logradouro + "\n CEP: " + this.CEP + "\n Bairro: " + this.bairro + "\n Cidade: " + this.cidade + "\n Estado: " + this.estado + "\n";
		
	}
	
	/**
	 * 
	 * @return CPF no formato XXXXXXXXXXX, no qual X representa um algarismo
	 */
	public String getChave() {
		return this.CPF;
	} 

	/**
	 * Retorna o número da identidade em formato String
	 * @return numero da identidade
	 */
	public String getIdentidade() {
		return this.identidade;
	}

	/**
	 * Retorna a data de nascimento da pessoa
	 * @return data de nascimento
	 */
	public Data getDataNascimento() {
		return this.dataNascimento;
	}

	/**
	 * Retorna o nome da pessoa
	 * @return nome da pessoa
	 */
	public String getNome() {
		return this.nome;
	}

	/**
	 * Retorna o nome do órgão expedidor da identidade
	 * @return nome do órgão expedidor
	 */
	public String getExpedidor() {
		return this.orgaoExpedidor;
	}

	/**
	 * Retorna o CPF no formato XXXXXXXXXXX, onde X representa um algarismo
	 * @return
	 */
	public String getCPF() {
		return this.CPF;
	}

	/**
	 * Retorna o CEP no formato XXXXXXX, no qual X representa um algarismo
	 * @return CEP
	 */
	public String getCEP() {
		return this.CEP;
	}

	/**
	 * Retorna o nome do logradouro
	 * @return nome do logradouro
	 */
	public String getLogradouro() {
		return this.logradouro;
	}

	/**
	 * Retorna o nome da cidade
	 * @return nome da cidade
	 */
	public String getCidade() {
		return this.cidade;
	}
	/**
	 * Retorna o nome do bairro.
	 * @return nome do bairro
	 */
	public String getBairro() {
		return this.bairro;
	}
	/**
	 * Retorna o nome do estado
	 * @return nome do estado
	 */
	public String getEstado() {
		return this.estado;
	}
	
	/**
	 * Atualiza apenas os dados passados por parâmetro que forem diferentes dos dados atualmente 
	 * representados no objeto.
	 * @param nome nome
	 * @param dataNascimento data de nascimento no formato DDMMAAA
	 * @param identidade identidade
	 * @param orgaoExpedidor órgão expedidor da identidade
	 * @param CPF CPF no formato XXXXXXXX, no qual X representa um algarismo
	 * @param logradouro logradouro
	 * @param CEP CEP no formato 
	 * @param bairro bairro
	 * @param cidade cidade
	 * @param estado estado
	 */
	public void atualizar(String nome, Data dataNascimento, String identidade, String orgaoExpedidor, String CPF, String logradouro, 
			String CEP, String bairro, String cidade, String estado) throws Exception{
		if(this.nome != nome)
			this.nome = limitaTamanhoNome(nome);
		if(this.dataNascimento != dataNascimento)
			this.dataNascimento = dataNascimento;
		if(this.identidade != identidade)
			this.identidade = limitaTamanhoIdentidade(identidade);
		if(this.orgaoExpedidor != orgaoExpedidor)
			this.orgaoExpedidor = limitaTamanhoOrgaoExpedidor(orgaoExpedidor);
		if(this.CPF != CPF)
			this.CPF = validaCPF(CPF);
		if(this.logradouro != logradouro)
			this.logradouro = limitaTamanhoLogradouro(logradouro);
		if(this.CEP != CEP)
			this.CEP = validaCEP(CEP);
		if(this.bairro != bairro)
			this.bairro = limitaTamanhoBairro(bairro);
		if(this.cidade != cidade)
			this.cidade = limitaTamanhoCidade(cidade);
		if(this.estado != estado)
			this.estado = limitaTamanhoEstado(estado);
	}

}
